import Vector from "../vector/index";

class Matrix {
    protected _data: number[][];

    /**
     * 矩阵类
     * @param data 二维数字数组
     */
    constructor(data: number[][]) {
        this._data = data;
    }

    private get shape() {
        return [this._data.length, this._data[0].length];
    }

    /** 矩阵行数 */
    get row() {
        return this.shape[0];
    }

    /** 矩阵列数 */
    get col() {
        return this.shape[1];
    }

    /** 元素个数 */
    get size() {
        return this.row * this.col;
    }

    /**
     * 根据索引获取行向量
     * @param row 行索引
     * @returns 
     */
    rowVector(row: number) {
        return new Vector(this._data[row]);
    }

    /**
     * 根据索引获取列向量
     * @param col 列索引
     * @returns 
     */
    colVector(col: number) {
        return new Vector(this._data.map(row => row[col]));
    }

    /**
     * 根据索引位置获取元素
     * @param position 位置索引
     * @returns 
     */
    getItem(position: [number, number]) {
        return this._data[position[0]][position[1]];
    }

    /**
     * 根据索引位置设置元素值
     * @param position 位置索引
     * @param value 设置值
     */
    setItem(position: [number, number], value: number) {
        this._data[position[0]][position[1]] = value;
        return this;
    }

    /**
     * 矩阵与标量乘法
     * @param k 
     * @returns 
     */
    mul(k: number) {
        return new Matrix(this._data.map(row => row.map(e => e * k)));
    }

    /**
     * 矩阵与标量除法
     * @param k 
     * @returns 
     */
    div(k: number) {
        return this.mul(1 / k);
    }

    /**
     * 矩阵取正
     * @returns 
     */
    pos() {
        return this.mul(1);
    }

    /**
     * 矩阵取负
     * @returns 
     */
    neg() {
        return this.mul(-1);
    }

    /**
     * 与矩阵的加法运算
     * @param another 
     * @returns 
     */
    add(another: Matrix) {
        if (this.shape[0] !== another.row || this.shape[1] !== another.col) {
            throw new Error("Matrix shape not match");
        }
        return new Matrix(this._data.map((row, i) => row.map((e, j) => e + another._data[i][j])));
    }

    /**
     * 与矩阵的减法运算
     * @param another 
     * @returns 
     */
    sub(another: Matrix) {
        return this.add(another.neg());
    }

    /**
     * 与向量的乘法运算
     * @param vector 
     * @returns 
     */
    mulVector(vector: Vector) {
        if (this.col !== vector.dimension) {
            throw new Error("Matrix shape not match");
        }
        return new Matrix(this._data.map(row => row.map((e, i) => e * vector.getItem(i))));
    }

    /**
     * 与矩阵的乘法运算
     * @param another 
     * @returns 
     */
    mulMatrix(another: Matrix) {
        if (this.col !== another.row) {
            throw new Error("Matrix shape not match");
        }
        const result = Matrix.zero(this.row, another.col);
        for (let i = 0; i < this.row; i++) {
            const rowList = this.rowVector(i);
            for (let j = 0; j < this.col; j++) {
                result.setItem([i, j], rowList.dotMul(another.colVector(j)));
            }
        }
        return result;
    }

    /**
     * 创建零矩阵
     * @param row 行数
     * @param col 列数
     * @returns 
     */
    static zero(row: number, col: number) {
        return new Matrix(Array.from({ length: row }, () => Array.from({ length: col }, () => 0)));
    }

    toString() {
        return this._data.map(row => row.join(" ")).join("\n");
    }
}

export default Matrix;
